`
    场景内划分若干 Layout 使用上下左右连接网格式格局组成
    TLayout 由 Button的二维数组组成，代表行列，
    TButton 游戏内的按键 由包含cc.Button组件的节点组成

    Tbutton 可以对应多个节点，可设置筛选条件(默认筛选条件是active = true)
    TLayout 可以没有节点
`

const { property, ccclass } = cc._decorator;

export enum DirectionCode {
    LEFT,
    RIGHT,
    UP,
    DOWN,
    ENTER,
    BACK,
    OTHER
}

export enum PriorityLevel {
    BeforeSystem,
    SystemLevel,
    AfterSystem
}

/**
 * Scene
 */
export enum ScenePriority {
    Bottom,
    Normal,
    Top
}

let getDirectionCode = function (directionCode: number): DirectionCode {
    switch (directionCode) {
        case cc.macro.KEY.left:
        case cc.macro.KEY.dpadLeft:
            return DirectionCode.LEFT;
        case cc.macro.KEY.right:
        case cc.macro.KEY.dpadRight:
            return DirectionCode.RIGHT;
        case cc.macro.KEY.up:
        case cc.macro.KEY.dpadUp:
            return DirectionCode.UP;
        case cc.macro.KEY.down:
        case cc.macro.KEY.dpadDown:
            return DirectionCode.DOWN;
        case cc.macro.KEY.enter:
        case cc.macro.KEY.dpadCenter:
            return DirectionCode.ENTER;
        case cc.macro.KEY.back:
        case cc.macro.KEY.backspace:
        case cc.macro.KEY.num0:
        case 48:
            return DirectionCode.BACK;
        default:
            return DirectionCode.OTHER;
    }
}

export type Site = {
    row: number,
    line: number,
}

export type MoveInfo = {
    startScene: TScene;
    targetScene: TScene;
    startLayout: TLayout;
    targetLayout: TLayout;
    startBtn: TButton;
    targetBtn: TButton;
    isArrive: boolean; //是否已抵达
    isExecuteeEvent: boolean; //是否响应
};
/**
 * @param DirectionCode : 按键键值 
 * @param layout : 当前布局 
 * @param button : 当前按键 
 */
export type KeyEventParameters = {
    DirectionCode: DirectionCode | null
    layout: TLayout | null
    button: TButton
    clickCount: number
};


export enum SceneState {
    normal, //显示手指并且正常响应按钮
    hideFinger,//隐藏手指 正常响应按钮
    pause,//显示手指，不响应按钮
    stop,//不显示手指，不响应按钮
}

export enum FingerStyle {
    finger = "finger",
    hand = "hand",
}

export enum HandAnimaName {
    handCatch = "handCatch",
    handBreathing = "handBreathing",
    handLoosen = "handLoosen"
}

export class TButton {
    node: cc.Node;  //包含cc.Button组件的节点
    offset: cc.Vec3; // 手指移动到按钮上的偏移位置

    keyDownEvents: ((paramenters: KeyEventParameters) => void)[][];//当按下按键后的回调
    keyUpEvents: ((paramenters: KeyEventParameters) => void)[];//当按下按键后的回调
    keyLongPressEvents: ((paramenters: KeyEventParameters, dt: number) => void)[];//当按下按键后的回调
    fingerMoveOutEvents: ((paramenters: MoveInfo | null) => void)[];//当手指从此按钮移开
    fingerAfterMoveInEvents: ((paramenters: MoveInfo | null) => void)[]; //当手指移入此按钮之后
    fingerbeforeMoveInEvents: ((paramenters: MoveInfo | null) => void)[]; //当手指移入此按钮之前
    parentLayout: TLayout;

    constructor(node: cc.Node, monitor: DirectionCode[] = [DirectionCode.LEFT, DirectionCode.RIGHT, DirectionCode.UP, DirectionCode.DOWN, DirectionCode.ENTER, DirectionCode.BACK]) {
        if (!node) {
            cc.error("node 不能为null")
        }
        this.node = node;
        this.offset = cc.v3(0, 0);

        //处理手指在节点上时，使用遥控器后的移动和enter操作
        monitor && this.addKeyDownEvent((paramenters: KeyEventParameters) => {
            let startLayout: TLayout = this.parentLayout,
                startButton: TButton = this,
                targetLayout: TLayout, targetButton: TButton | null,
                context = SceneController.instance;
            switch (paramenters.DirectionCode) {
                case DirectionCode.LEFT:
                    if (monitor.includes(DirectionCode.LEFT)) {
                        if (this.parentLayout.site.line > 0) {
                            this.parentLayout.site.line--;
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfo({ startScene: this.parentLayout.parentScene, targetScene: this.parentLayout.parentScene, startLayout: this.parentLayout, targetLayout: this.parentLayout, startBtn: startButton, targetBtn: targetButton, isArrive: false, isExecuteeEvent: true })
                        } else {
                            //换Layout
                            this.parentLayout.parentScene.changeLayout(DirectionCode.LEFT);
                        }
                    }
                    break;
                case DirectionCode.RIGHT:
                    if (monitor.includes(DirectionCode.RIGHT)) {
                        if (this.parentLayout.site.line < this.parentLayout.buttonList[this.parentLayout.site.row].length - 1) {
                            this.parentLayout.site.line++;
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfo({ startScene: this.parentLayout.parentScene, targetScene: this.parentLayout.parentScene, startLayout: this.parentLayout, targetLayout: this.parentLayout, startBtn: startButton, targetBtn: targetButton, isArrive: false, isExecuteeEvent: true })
                        } else {
                            //换Layout 
                            this.parentLayout.parentScene.changeLayout(DirectionCode.RIGHT);
                        }
                    }
                    break;
                case DirectionCode.UP:
                    if (monitor.includes(DirectionCode.UP)) {
                        if (this.parentLayout.site.row > 0) {
                            this.parentLayout.site.row--;
                            this.parentLayout.site.line = Math.min(this.parentLayout.site.line, this.parentLayout.buttonList[this.parentLayout.site.row].length - 1)
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfo({ startScene: this.parentLayout.parentScene, targetScene: this.parentLayout.parentScene, startLayout: this.parentLayout, targetLayout: this.parentLayout, startBtn: startButton, targetBtn: targetButton, isArrive: false, isExecuteeEvent: true })
                        } else {
                            //换Layout 
                            this.parentLayout.parentScene.changeLayout(DirectionCode.UP);
                        }
                    }
                    break;
                case DirectionCode.DOWN:
                    if (monitor.includes(DirectionCode.DOWN)) {
                        if (this.parentLayout.site.row < this.parentLayout.buttonList.length - 1) {
                            this.parentLayout.site.row++;
                            this.parentLayout.site.line = Math.min(this.parentLayout.site.line, this.parentLayout.buttonList[this.parentLayout.site.row].length - 1)
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfo({ startScene: this.parentLayout.parentScene, targetScene: this.parentLayout.parentScene, startLayout: this.parentLayout, targetLayout: this.parentLayout, startBtn: startButton, targetBtn: targetButton, isArrive: false, isExecuteeEvent: true })
                        } else {
                            //换Layout 
                            this.parentLayout.parentScene.changeLayout(DirectionCode.DOWN);
                        }
                    }
                    break;
                case DirectionCode.ENTER:
                    //触发按钮事件
                    if (monitor.includes(DirectionCode.ENTER)) {
                        let event = new cc.Event.EventTouch([], false);
                        event.getLocation = () => this.node.convertToWorldSpaceAR(cc.Vec2.ZERO);
                        event.target = this.node;
                        event.currentTarget = this.node;
                        this.node.emit(cc.Node.EventType.TOUCH_START, event);
                        this.node && this.node.emit(cc.Node.EventType.TOUCH_END, event);
                    }
                    break;
                case DirectionCode.BACK:
                    if (monitor.includes(DirectionCode.BACK)) {
                        let onback = this.parentLayout.onBack ? this.parentLayout.onBack : this.parentLayout.parentScene.onBack;
                        if (onback) {
                            if (onback instanceof cc.Node) {
                                let event = new cc.Event.EventTouch([], false);
                                event.getLocation = () => this.node.convertToWorldSpaceAR(cc.Vec2.ZERO);
                                event.target = onback;
                                onback.emit(cc.Node.EventType.TOUCH_START, event);
                                onback.emit(cc.Node.EventType.TOUCH_END, event);
                            } else {
                                onback();
                            }
                        }
                    }
                    break;
            }
        }, 0, PriorityLevel.SystemLevel);
    }

    public static addScaleEffect(tbtn: TButton, scale: number = 1.05) {
        tbtn.addAfterFingerInEvent((para: MoveInfo) => {
            cc.tween(para.targetBtn.node)
                .to(0.1, { scale: scale })
                .start();
        })
        tbtn.addFingerOutEvent((para: MoveInfo) => {
            cc.tween(para.startBtn.node)
                .to(0.1, { scale: 1 })
                .start();
        })
        return tbtn;
    }

    clear() {
        this.node = null;
        this.offset = null;
        this.parentLayout = null;
        this.keyDownEvents = null;
        this.keyUpEvents = null
        this.keyDownEvents = null
        this.keyLongPressEvents = null
        this.fingerAfterMoveInEvents = null
        this.fingerMoveOutEvents = null
        this.fingerbeforeMoveInEvents = null
    }

    /**
     * 添加一个长按按键后的回调
     * @param event 要添加的事件
     * @param priority 事件优先级 越小越靠前
     */
    addKeyLongPressEvent(event: (paramenters: KeyEventParameters, dt: number) => void, priority?: number) {
        if (!this.keyLongPressEvents) this.keyLongPressEvents = [];
        if (priority == undefined || priority >= this.keyLongPressEvents.length) {
            priority = this.keyLongPressEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        this.keyLongPressEvents.splice(priority, 0, event);
    }
    /**
     * 添加一个按下按键后的回调
     * @param event 要添加的事件
     * @param priority 事件优先级 越小越靠前
     */
    addKeyDownEvent(event: (paramenters: KeyEventParameters) => void, priorityIndex?: number, priorityLevel: PriorityLevel = PriorityLevel.AfterSystem) {
        if (!this.keyDownEvents) this.keyDownEvents = [[], [], []];
        if (priorityIndex == undefined || priorityIndex >= this.keyDownEvents[priorityLevel].length) {
            priorityIndex = this.keyDownEvents[priorityLevel].length;
        } else if (priorityIndex < 0) {
            priorityIndex = 0;
        }
        this.keyDownEvents[priorityLevel].splice(priorityIndex, 0, event);
    }

    /**
    * 添加一个抬起按键后的回调
    * @param event 要添加的事件
    * @param priority 事件优先级 越小越靠前
    */
    addKeyUpEvent(event: (paramenters: KeyEventParameters) => void, priority?: number) {
        if (!this.keyUpEvents) this.keyUpEvents = [];
        if (priority == undefined || priority >= this.keyUpEvents.length) {
            priority = this.keyUpEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        this.keyUpEvents.splice(priority, 0, event);
    }

    /**
     * 添加一个手指移入按键前的回调
     * @param event 要添加的事件
     * @param priority 事件优先级 越小越靠前
     */
    addAfterFingerInEvent(event: (paramenters: MoveInfo | null) => void, priority?: number) {
        if (!this.fingerAfterMoveInEvents) this.fingerAfterMoveInEvents = [];
        if (priority == undefined || priority >= this.fingerAfterMoveInEvents.length) {
            priority = this.fingerAfterMoveInEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        this.fingerAfterMoveInEvents.splice(priority, 0, event);
    }

    /**
    * 添加一个手指移入按键前的回调
    * @param event 要添加的事件
    * @param priority 事件优先级 越小越靠前
    */
    addBeforeFingerInEvent(event: (paramenters: MoveInfo | null) => void, priority?: number) {
        if (!this.fingerbeforeMoveInEvents) this.fingerbeforeMoveInEvents = [];
        if (priority == undefined || priority >= this.fingerbeforeMoveInEvents.length) {
            priority = this.fingerbeforeMoveInEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        this.fingerbeforeMoveInEvents.splice(priority, 0, event);
    }


    /**
     * 添加一个手指移出按键后的回调
     * @param event 要添加的事件
     * @param priority 事件优先级 越小越靠前
     */
    addFingerOutEvent(event: (paramenters: MoveInfo | null) => void, priority?: number) {
        if (!this.fingerMoveOutEvents) this.fingerMoveOutEvents = [];
        if (priority == undefined || priority >= this.fingerMoveOutEvents.length) {
            priority = this.fingerMoveOutEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        this.fingerMoveOutEvents.splice(priority, 0, event);
    }

    callKeyDownEvents(DirectionCode: DirectionCode | null, layout: TLayout | null, clickCount: number) {
        if (!this.keyDownEvents) return;
        let paramenters: KeyEventParameters = { DirectionCode: DirectionCode, layout: layout, button: this, clickCount: clickCount }
        this.keyDownEvents.forEach(level => level.forEach(item => item(paramenters)));
    }
    callKeyUpEvents(DirectionCode: DirectionCode, layout: TLayout | null) {
        if (!this.keyUpEvents) return;
        let paramenters: KeyEventParameters = { DirectionCode: DirectionCode, layout: layout, button: this, clickCount: null }
        this.keyUpEvents.forEach(item => item(paramenters));
    }
    callKeyLongPressEvents(DirectionCode: DirectionCode, layout: TLayout, dt: number) {
        if (!this.keyLongPressEvents) return;
        let paramenters: KeyEventParameters = { DirectionCode: DirectionCode, layout: layout, button: this, clickCount: null }
        this.keyLongPressEvents.forEach(item => item(paramenters, dt));
    }

    callfingerAfterMoveInEvents(moveInfo: MoveInfo) {
        if (!this.fingerAfterMoveInEvents) return;
        this.fingerAfterMoveInEvents.forEach(item => item(moveInfo));
    }
    callfingerBeforeMoveInEvents(moveInfo: MoveInfo) {
        if (!this.fingerbeforeMoveInEvents) return;
        this.fingerbeforeMoveInEvents.forEach(item => item(moveInfo));
    }

    callFingerMoveOutEvents(moveInfo: MoveInfo | null) {
        if (!this.fingerMoveOutEvents) return;
        this.fingerMoveOutEvents.forEach(item => item(moveInfo));
    }

}

export class TLayout {
    name: string; // Layout的名字
    site: Site; //当前选中的按键位置 [行,列]
    parentScene: TScene;
    onBack: (() => void) | cc.Node

    private _buttonList: TButton[][] | null = null; //按键
    public get buttonList(): TButton[][] | null {
        return this._buttonList;
    }
    public set buttonList(value: TButton[][] | null) {
        if (value) {
            if (value.length > 0 && value[0].length > 0) {
                this._buttonList = value;
                value.forEach(row => row.forEach(but => but.parentLayout = this));
            } else {
                cc.error("Layout :" + this.name + "buttonList是空的！");
                return;
            }
        } else {
            this.parentScene && this.parentScene.currLayout == this && this.parentScene.changeLayout(DirectionCode.OTHER);
            this._buttonList = null;
        }
    }

    public get currButton(): TButton | null {
        if (this.buttonList) {
            return this.buttonList[this.site.row][this.site.line];
        } else {
            return null;
        }
    }

    public get currButtonRow(): TButton[] {
        if (this.buttonList) {
            return this.buttonList[this.site.row];
        } else {
            return null;
        }
    }

    constructor(LayoutName = "def") {
        this.name = LayoutName;
        this.site = { row: 0, line: 0 };
    }

    changeButton(dc: DirectionCode) {
        let targetBtn: TButton = null;
        let dire: any[], context = SceneController.instance;
        let startBtn = this.currButton

        switch (dc) {
            case DirectionCode.LEFT:
                for (let i = this.site.line - 1; i >= 0; i--) {
                    if (this.buttonList[this.site.row][i]) {
                        this.site.line = i;
                        break;
                    }
                }
                break;
            case DirectionCode.RIGHT:
                for (let i = this.site.line + 1; i < this.buttonList[this.site.row].length; i++) {
                    if (this.buttonList[this.site.row][i]) {
                        this.site.line = i;
                        break;
                    }
                }
                break;
            case DirectionCode.UP:
                for (let i = this.site.row - 1; i >= 0; i--) {
                    if (this.buttonList[i][this.site.line]) {
                        this.site.row = i;
                        break;
                    }
                }
                break;
            case DirectionCode.DOWN:
                for (let i = this.site.row + 1; i <= this.buttonList.length; i++) {
                    if (this.buttonList[i][this.site.line]) {
                        this.site.row = i;
                        break;
                    }
                }
                break;
        }

        if (targetBtn) {
            context.setCurrentMoveInfo({ startScene: this.parentScene, targetScene: this.parentScene, startLayout: this, targetLayout: this, startBtn: startBtn, targetBtn: targetBtn, isArrive: false, isExecuteeEvent: true })
            return true;
        } else {
            return false;
        }
    }

    clear() {
        this.name = null;
        this.site = null;
        this.parentScene = null;
        this.onBack = null;
        if (this.buttonList)
            for (let i = 0; i < this.buttonList.length; i++) {
                for (let j = 0; j < this.buttonList[i].length; j++) {
                    this.buttonList[i][j].clear();
                }
            }
    }
}

export class TScene {
    sceneName: string;
    sceneState: SceneState;
    fingerStyle: FingerStyle;
    onBack: (() => void) | cc.Node //当按下返回键时的回调 如果当前layout存在onBack，则不触发scene的onback
    site: Site; //当前选择的Layout
    private _layouts: TLayout[][]; //此Scene(场景)下的Layout(布局)
    priority: ScenePriority;

    _KeyDownTbtn: TButton;

    public get layouts(): TLayout[][] {
        return this._layouts;
    }
    public set layouts(value: TLayout[][]) {
        if (value) {
            // if (this._layouts) this._layouts.forEach(row => row.forEach(item => item.clear()))
            this._layouts = value;
            this._layouts.forEach(row => row.forEach(item => item.parentScene = this))
        }
    }

    public get currLayout(): TLayout {
        return this.layouts[this.site.row][this.site.line];
    }

    public get isEmpty(): boolean {
        for (let i = 0; i < this.layouts.length; i++) {
            for (let j = 0; j < this.layouts[i].length; j++) {
                if (this.layouts[i][j]) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 创建Tscne 
     * @param sceneName scene名字
     */
    constructor(sceneName: string, sceneState: SceneState = SceneState.normal) {
        this.sceneName = sceneName;
        this.site = { row: 0, line: 0 };
        this.sceneState = sceneState
        this.layouts = [];
        this.fingerStyle = FingerStyle.finger;
        this.priority = ScenePriority.Normal;
    }

    changeLayout(dc: DirectionCode) {
        let targetLayout: TLayout = null;
        let context = SceneController.instance;
        let startLayout = this.layouts[this.site.row][this.site.line];
        switch (dc) {
            case DirectionCode.LEFT:
                for (let i = this.site.line - 1; i >= 0; i--) {
                    if (this.layouts[this.site.row][i].buttonList) {
                        this.site.line = i;
                        break;
                    }
                }
                break;
            case DirectionCode.RIGHT:
                for (let i = this.site.line + 1; i < this.layouts[this.site.row].length; i++) {
                    if (this.layouts[this.site.row][i].buttonList) {
                        this.site.line = i;
                        break;
                    }
                }
                break;
            case DirectionCode.UP:
                for (let i = this.site.row - 1; i >= 0; i--) {
                    if (this.layouts[i].length > 0) {
                        this.site.line = Math.min(this.site.line, this.layouts[i].length - 1);
                        if (this.layouts[i][this.site.line] && this.layouts[i][this.site.line].buttonList) {
                            this.site.row = i;
                            break;
                        } else {
                            let closestIndex = -1;
                            this.layouts[i].forEach((item, index) => {
                                if (item.buttonList && (Math.abs(this.site.line - index) < closestIndex || closestIndex == -1))
                                    closestIndex = index;
                            })
                            if (closestIndex != -1 && closestIndex != this.site.line) {
                                this.site.row = i;
                                this.site.line = closestIndex;
                                break;
                            }
                        }
                    }
                }
                break;
            case DirectionCode.DOWN:
                for (let i = this.site.row + 1; i < this.layouts.length; i++) {
                    if (this.layouts[i].length > 0) {
                        this.site.line = Math.min(this.site.line, this.layouts[i].length - 1)
                        if (this.layouts[i][this.site.line] && this.layouts[i][this.site.line].buttonList) {
                            this.site.row = i;
                            break;
                        } else {
                            let closestIndex = -1;
                            this.layouts[i].forEach((item, index) => {
                                if (item.buttonList && (Math.abs(this.site.line - index) < closestIndex || closestIndex == -1))
                                    closestIndex = index;
                            })
                            if (closestIndex != -1 && closestIndex != this.site.line) {
                                this.site.row = i;
                                this.site.line = closestIndex;
                                break;
                            }
                        }
                    }
                }
                break;
            case DirectionCode.OTHER:
                let isOk = false;
                let tempSite = { row: this.site.row, line: this.site.line };
                A: for (let i = this.site.row; i >= 0; i--) {
                    for (let j = this.site.line; j >= 0; j--) {
                        if ((i != tempSite.row || j != tempSite.line) && this.layouts[i][j].buttonList) {
                            this.site.row = i;
                            this.site.line = j;
                            isOk = true;
                            break A;
                        }
                    }
                    for (let j = this.site.line; j < this.layouts[i].length; j++) {
                        if ((i != tempSite.row || j != tempSite.line) && this.layouts[i][j].buttonList) {
                            this.site.row = i;
                            this.site.line = j;
                            isOk = true;
                            break A;
                        }
                    }
                }

                if (!isOk) {
                    B: for (let i = this.site.row; i < this.layouts.length; i++) {
                        for (let j = this.site.line - 1; j >= 0; j--) {
                            if ((i != tempSite.row || j != tempSite.line) && this.layouts[i][j].buttonList) {
                                this.site.row = i;
                                this.site.line = j;
                                isOk = true;
                                break B;
                            }
                        }
                        for (let j = this.site.line; j < this.layouts[i].length; j++) {
                            if ((i != tempSite.row || j != tempSite.line) && this.layouts[i][j].buttonList) {
                                this.site.row = i;
                                this.site.line = j;
                                isOk = true;
                                break B;
                            }
                        }
                    }
                }
                if (!isOk) {
                    cc.error("没有了。。。");
                    this.sceneState = SceneState.stop;
                    this.apply();
                }
                break;
        }
        targetLayout = this.layouts[this.site.row][this.site.line];
        if (targetLayout) {
            if (SceneController.instance.scenes[SceneController.instance.scenes.length - 1] == this) {
                context.setCurrentMoveInfo({ startScene: this, targetScene: this, startLayout: startLayout, targetLayout: targetLayout, startBtn: startLayout.currButton, targetBtn: targetLayout.currButton, isArrive: false, isExecuteeEvent: true })
            }
            return true;
        } else {
            return false;
        }
    }

    findLayoutByName(layoutName: string): TLayout {
        let findLayout: TLayout;
        A: for (let i = 0; i < this.layouts.length; i++) {
            for (let j = 0; j < this.layouts[i].length; j++) {
                if (this.layouts[i][j].name == layoutName) {
                    findLayout = this.layouts[i][j];
                    break A;
                }
            }
        }
        return findLayout;
    }

    /**
    * 在指定的位置添加一个Layout
    * @param layout 
    * @param site 
    */
    addLayout(layout: TLayout, site: Site = { row: 0, line: 0 }) {
        if (site.row > this.layouts.length) site.row = this.layouts.length;
        if (!this.layouts[site.row]) this.layouts[site.row] = [];
        if (this.layouts[site.row][site.line]) this.layouts[site.row][site.line].clear();
        this.layouts[site.row][site.line] = layout;
        this.layouts[site.row][site.line].parentScene = this;
    }

    /**
     * 
     * @param layout 
     * @param site 
     * @param pushType 0 :row级别的push，!=0 :line级别的push
     */
    pushLayout(layout: TLayout, site: Site, pushType: number = 0) {
        if (site.row > this.layouts.length) site.row = this.layouts.length;
        if (pushType == 0) {
            this.layouts.splice(site.row, 0, [layout])
        } else {
            this.layouts[site.row].splice(site.line, 0, layout);
        }
        layout.parentScene = this;
    }

    onKeyDown(event: cc.Event.EventKeyboard, clickCount: number) {
        if (this.sceneState == SceneState.pause || this.sceneState == SceneState.stop) return;
        let directionCode: DirectionCode = getDirectionCode(event.keyCode);
        this._KeyDownTbtn = this.currLayout.currButton;
        this.currLayout.currButton.callKeyDownEvents(directionCode, this.currLayout, clickCount);
    }

    onKeyUp(event: cc.Event.EventKeyboard, context: SceneController) {
        if (this._KeyDownTbtn != this.currLayout.currButton) return;
        if (this.sceneState == SceneState.pause || this.sceneState == SceneState.stop) return;
        let code: DirectionCode = getDirectionCode(event.keyCode);
        if (code != null && this.currLayout && this.currLayout.currButton) {
            this.currLayout.currButton.callKeyUpEvents(code, this.currLayout);
        }
    }

    onKeyLongPress(DirectionCode: number, dt: number) {
        if (this._KeyDownTbtn != this.currLayout.currButton) return;
        if (this.sceneState == SceneState.pause || this.sceneState == SceneState.stop) return;
        if (this.currLayout && this.currLayout.currButton) {
            this.currLayout.currButton.callKeyLongPressEvents(DirectionCode, this.currLayout, dt);
        }
    }

    /**
     * 清除此Scene下的Layout
     */
    removeThis() {
        SceneController.instance.closeScene(this.sceneName);
        for (let i = 0; i < this.layouts.length; i++) {
            for (let j = 0; j < this.layouts[i].length; j++) {
                this.layouts[i][j].clear();
            }
        }
    }

    joinController() {
        SceneController.instance.openScene(this);
    }

    apply(isExecuteeEvent: boolean = true) {
        SceneController.instance.applyScene(this.sceneName, isExecuteeEvent);
    }
}
@ccclass
export default class SceneController extends cc.Component {

    lastDt: number = 0.01;

    scenes: TScene[] = [];

    recordDirectionCode: number = -1; //记录双击的键值

    _currentMoveInfo: MoveInfo | null = null; //手指的移动信息

    lastDirectionCode = []; //上一次按下的按键键值

    lastKeyDownSceneName; //上次按下按键时的SceneName

    handNode: cc.Node;
    fingerTip: cc.Node;
    clickCount: number;

    public get fingerShow(): boolean {
        return this.finger.active;
    }

    public set fingerShow(show: boolean) {
        this.finger.active = show;
    }

    public get finger(): cc.Node {
        let topScene = this.scenes[this.scenes.length - 1];
        if (topScene) {
            let ret = null;
            switch (topScene.fingerStyle) {
                case FingerStyle.finger:
                    ret = this.fingerTip;
                    break;
                case FingerStyle.hand:
                    ret = this.handNode;
                    break;
            }
            return ret;
        } else {
            return this.fingerTip;
        }
    }

    public get currentMoveInfo(): MoveInfo | null {
        return this._currentMoveInfo;
    }

    public static getInstance(): SceneController {
        return this.instance;
    }

    static instance: SceneController = null;

    public _inService: boolean = true;


    public get inService(): boolean {
        return this._inService;
    }


    public set inService(v: boolean) {
        this._inService = v;
        this.lastDirectionCode = [];
    }


    onLoad() {

        SceneController.instance = this;
        cc.game.addPersistRootNode(this.node);

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this._keyDown, this);
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this._keyUp, this);

        let fingerTip = new cc.Node(FingerStyle.finger);
        let finger = new cc.Node("finger").addComponent(cc.Sprite).node;
        let effect = new cc.Node("effect").addComponent(cc.Sprite).node;

        fingerTip.group = "UI";
        finger.group = "UI";
        effect.group = "UI";

        fingerTip.parent = this.node;
        fingerTip.position = cc.v3(-100, 100);
        effect.parent = fingerTip;
        finger.parent = fingerTip;
        fingerTip.scale = 0.7;
        let fingerAnimation = fingerTip.addComponent(cc.Animation);
        cc.resources.load("finger/finger", cc.SpriteAtlas, (err, spriteAtlas: cc.SpriteAtlas) => {
            let effects = spriteAtlas.getSpriteFrames();
            let fingerFrame = spriteAtlas.getSpriteFrame("finger");
            finger.getComponent(cc.Sprite).spriteFrame = fingerFrame;
            effects.length--;
            let clip = new cc.AnimationClip();
            clip["_duration"] = 1.25;
            clip.name = "fingerAnim";
            clip.sample = 16;
            clip.wrapMode = cc.WrapMode.Loop;
            clip.curveData = {
                paths: {
                    effect: {
                        comps: {
                            "cc.Sprite": {
                                spriteFrame: [{ frame: 0, value: effects[0] }, { frame: 0.25, value: effects[1] }
                                    , { frame: 0.5, value: effects[2] }, { frame: 0.75, value: effects[3] }, { frame: 1, value: effects[4] }]
                            }
                        }
                    }
                    , finger: { props: { position: [{ frame: 0, value: [45, -20, 0] }, { frame: 0.625, value: [45, -50, 0] }, { frame: 1.25, value: [45, -20, 0] },] } }
                }
            };
            fingerAnimation.addClip(clip);
            fingerAnimation.play(clip.name);
        })
        this.fingerTip = fingerTip;

        cc.resources.load("finger/hand", cc.Prefab, (err, hand: cc.Prefab) => {
            if (err) return;
            this.handNode = cc.instantiate(hand);
            this.handNode.position = cc.v3(720, 300);
            this.handNode.parent = this.node;
            this.handNode.opacity = 240
            this.handNode.active = false;
        })

        cc.director.on(cc.Director.EVENT_BEFORE_DRAW, this.updateThis.bind(this));
    }


    setCurrentMoveInfo(moveInfo: MoveInfo) {
        this._currentMoveInfo = moveInfo;
        if (moveInfo && moveInfo.isExecuteeEvent) {
            if (this.currentMoveInfo.startBtn) {
                if (this.currentMoveInfo.targetBtn && this.currentMoveInfo.targetBtn.node != this.currentMoveInfo.startBtn.node) {
                    this.currentMoveInfo.startBtn.callFingerMoveOutEvents(moveInfo);
                }
            }
            if (moveInfo.targetBtn) {
                if (this.currentMoveInfo.startBtn && this.currentMoveInfo.startBtn.node != this.currentMoveInfo.targetBtn.node) {
                    moveInfo.targetBtn.callfingerBeforeMoveInEvents(moveInfo);
                }
            }
        }
    }

    /**
     * 打开scene,并且选择初始Layout 弹窗也是Scene
     * @param scene 
     * @param curSite 初始Layout的位置
     */
    public openScene(scene: TScene) {
        this.node.setSiblingIndex(cc.macro.MAX_ZINDEX);
        if (scene.layouts.length == 0) {
            return cc.error("scene: " + scene.sceneName + " 不存在Layout");
        };

        let exist = this.scenes.findIndex((item) => item.sceneName == scene.sceneName) >= 0;
        if (exist) {
            return cc.error("scene:" + scene.sceneName + "已存在");
        };

        cc.log(scene.sceneName + " open");
        let startScene: TScene;

        switch (scene.priority) {
            case ScenePriority.Top:
                startScene = this.scenes[this.scenes.length - 1];
                this.scenes.push(scene);
                break;
            case ScenePriority.Normal:
                if (this.scenes[this.scenes.length - 1] && this.scenes[this.scenes.length - 1].priority == ScenePriority.Top) {
                    let normalIndex = -1;
                    for (let i = this.scenes.length - 1; i >= 0; i--) {
                        if (this.scenes[i].priority == ScenePriority.Normal) {
                            normalIndex = i;
                            break;
                        }
                    }
                    this.scenes.splice(normalIndex + 1, 0, scene);
                } else {
                    startScene = this.scenes[this.scenes.length - 1];
                    this.scenes.push(scene);
                }
                break;
            case ScenePriority.Bottom:
                let bottomIndex = -1;
                for (let i = this.scenes.length - 1; i >= 0; i--) {
                    if (this.scenes[i].priority == ScenePriority.Bottom) {
                        bottomIndex = i;
                        break;
                    }
                }
                this.scenes.splice(bottomIndex + 1, 0, scene);
                break;
        }

        let targetScene = this.scenes[this.scenes.length - 1];
        this.applyFingerStyle(targetScene.fingerStyle);
        this.fingerShow = targetScene.sceneState == SceneState.normal || targetScene.sceneState == SceneState.pause;
        this.setCurrentMoveInfo({ startScene: startScene, targetScene: targetScene, startLayout: startScene?.currLayout ?? null, targetLayout: targetScene.currLayout, startBtn: startScene?.currLayout?.currButton ?? null, targetBtn: targetScene.currLayout.currButton, isArrive: false, isExecuteeEvent: true })
    }
    /**
     * 关闭Scene 手指移动到上一个scene
     * @param name 
     */
    public closeScene(closeName?: string) {
        let oldSceneIndex: number;
        if (closeName) {
            oldSceneIndex = this.scenes.findIndex(item => item.sceneName == closeName);
        } else {
            oldSceneIndex = this.scenes.length - 1;
        }
        if (oldSceneIndex < 0) return;
        //close的是不是栈顶的Scene 只有是栈顶的scene被close，才更新moveinfo和finger
        let isCloseTop: boolean = oldSceneIndex == this.scenes.length - 1;
        cc.log(this.scenes[oldSceneIndex].sceneName + " close");

        this.scenes.splice(oldSceneIndex, 1);

        this.lastDirectionCode = [];
        if (this.scenes.length == 0) {
            this.setCurrentMoveInfo(null);
            this.fingerShow = false;
            return;
        }

        if (isCloseTop) {
            let newScene = this.scenes[this.scenes.length - 1];
            this.applyFingerStyle(newScene.fingerStyle);
            this.fingerShow = newScene.sceneState == SceneState.normal || newScene.sceneState == SceneState.pause;
            if (newScene.fingerStyle == FingerStyle.hand && this.fingerShow) {
                this.playHandAnim(HandAnimaName.handBreathing);
            }
            this.setCurrentMoveInfo({ startScene: null, targetScene: newScene, startLayout: null, targetLayout: newScene.currLayout, startBtn: null, targetBtn: newScene.currLayout.currButton, isArrive: false, isExecuteeEvent: true });
        }
    }

    public getSceneByName(sceneName: string) {
        return this.scenes.find(itme => itme.sceneName == sceneName);
    }

    /**
     *  引用scene的修改
     * @param sceneName 
     */
    public applyScene(sceneName: string, isExecuteeEvent: boolean) {
        let sceneIndex: number = this.scenes.findIndex(itme => itme.sceneName == sceneName)
        let scene = this.scenes[sceneIndex];
        scene.layouts.forEach(row => row.forEach(layout => {
            if (layout.buttonList) {
                layout.site.row = Math.min(layout.site.row, layout.buttonList.length - 1);
                layout.site.line = Math.min(layout.buttonList[layout.site.row].length - 1, layout.site.line);
            }
        }))

        if (sceneIndex == this.scenes.length - 1) {
            this.applyFingerStyle(scene.fingerStyle);
            this.fingerShow = scene.sceneState == SceneState.normal || scene.sceneState == SceneState.pause;
            this.setCurrentMoveInfo({
                startScene: this.currentMoveInfo.targetScene,
                startLayout: this.currentMoveInfo.targetLayout,
                startBtn: this.currentMoveInfo.targetBtn,
                targetScene: scene,
                targetLayout: scene.currLayout,
                targetBtn: scene.currLayout.currButton,
                isArrive: false,
                isExecuteeEvent: isExecuteeEvent
            })
        }
    }

    removeHandAnim() {
        let anim = this.handNode.getComponent(cc.Animation);
        anim.clear();
    }

    /**
    * 置顶指定的scene
    */
    setSceneTop(sceneName: string) {
        let findScene = this.scenes.findIndex(itme => itme.sceneName == sceneName);
        if (findScene >= 0) {
            let oldTopScene = this.scenes[this.scenes.length - 1];
            let newTopScene = this.scenes.splice(findScene, 1)[0];
            this.scenes.push(newTopScene);
            this.fingerShow = newTopScene.sceneState == SceneState.normal || newTopScene.sceneState == SceneState.pause;
            this.applyFingerStyle(newTopScene.fingerStyle);
            this.setCurrentMoveInfo({ startScene: oldTopScene, targetScene: newTopScene, startLayout: oldTopScene.currLayout, targetLayout: newTopScene.currLayout, startBtn: oldTopScene.currLayout.currButton, targetBtn: newTopScene.currLayout.currButton, isArrive: false, isExecuteeEvent: true })
        }
    }

    /**
     * 只显示当前scene指定的手指样式
     */
    applyFingerStyle(style: FingerStyle) {
        this.node.children.forEach(item => item.active = item.name == style)
    }

    playHandAnim(name: HandAnimaName, callBack?: () => void) {
        let topScene = this.scenes[this.scenes.length - 1];
        if (topScene.fingerStyle == FingerStyle.hand && (topScene.sceneState == SceneState.normal || topScene.sceneState == SceneState.pause)) {
            let anim = this.handNode.getComponent(cc.Animation);
            anim.off(cc.Animation.EventType.FINISHED);
            anim.play(name);
            callBack && anim.once(cc.Animation.EventType.FINISHED, callBack, this);
        }
    }

    private _keyUp(event: cc.Event.EventKeyboard) {
        if (this.inService) {
            this.lastDirectionCode[event.keyCode] && (this.lastDirectionCode[event.keyCode] = 0);
            if (this.scenes.length > 0) {
                if (this.currentMoveInfo.targetScene.sceneName == this.lastKeyDownSceneName) {
                    this.scenes[this.scenes.length - 1].onKeyUp(event, this);
                }
            }
        }
    }

    private _keyDown(event: cc.Event.EventKeyboard) {
        if (this.inService) {
            if (this.scenes.length > 0) {
                if (!this.lastDirectionCode[event.keyCode]) {
                    this.lastDirectionCode[event.keyCode] = 1;

                    if (this.recordDirectionCode == event.keyCode) {
                        this.clickCount++;
                        this.unscheduleAllCallbacks();
                    } else {
                        this.clickCount = 0;
                        this.recordDirectionCode = event.keyCode;
                    }
                    this.scheduleOnce(() => this.recordDirectionCode = -1, 0.3);
                    this.lastKeyDownSceneName = this.currentMoveInfo.targetScene.sceneName;
                    this.scenes[this.scenes.length - 1].onKeyDown(event, this.clickCount);
                }
            }
        }
    }

    private keyDownLongPress(dt: number) {
        for (let i = 0; i < this.lastDirectionCode.length; i++) {
            if (this.lastDirectionCode[i] == 1) {
                this.scenes[this.scenes.length - 1].onKeyLongPress(getDirectionCode(i), dt);
            }
        }
    }

    /**
     * 更新移动手指位置，抵达终点时调用终点的movein事件
     * @param dt 
     */
    private updateFingerSite(dt: number) {
        if (this.currentMoveInfo) {
            let targetBtn = this.currentMoveInfo.targetBtn;
            let selfPoint = this.finger.convertToWorldSpaceAR(cc.Vec3.ZERO);
            let targetPoint = targetBtn.node.convertToWorldSpaceAR(cc.Vec3.ZERO);
            targetPoint.addSelf(targetBtn.offset);
            let tempTargetPoint = cc.v3(targetPoint.x, targetPoint.y);
            let sub = tempTargetPoint.sub(selfPoint), distance = sub.mag();
            let moveSpeed = cc.v2(2000, 0).lerp(cc.v2(8000, 0), distance / 800).x;
            //如果已抵达
            if (distance < this.lastDt * moveSpeed + 5) {
                if (!this.currentMoveInfo.isArrive && this.currentMoveInfo.isExecuteeEvent) {
                    targetBtn.callfingerAfterMoveInEvents(this.currentMoveInfo);
                }
                this.currentMoveInfo.isArrive = true;
                this.finger.position = this.finger.parent.convertToNodeSpaceAR(targetPoint);
            } else {
                this.finger.position = this.finger.position.add(sub.normalize().mul(moveSpeed * dt));
            }
        }
        this.lastDt = dt;
    }


    // update(dt: number) {
    //     if (this.inService) {
    //         this.updateFingerSite(dt);
    //         this.keyDownLongPress(dt);
    //     }
    // }

    updateThis(dt: number) {
        if (this.inService) {
            this.updateFingerSite(dt);
            this.keyDownLongPress(dt);
        }
    }
}
